﻿package plugins.mstandio.utils.combobox {
			
	import adobe.utils.ProductManager;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import gs.TweenLite;
		
	import plugins.mstandio.utils.combobox.ComboBoxElement;	
	import plugins.mstandio.utils.combobox.ComboBoxStyle;	
	import plugins.mstandio.utils.combobox.ComboBoxEvent;
	
	/**	 
	 * Combobox with default syles and basic actions
	 * @author mstandio
	 */
	public class ComboBox extends Sprite{	
		
		private var objects:Array;				
		private var style:ComboBoxStyle;		
		
		private var elements:Array;		
		
		private var mainElement:ComboBoxElement;						
		private var dropDownButton:Sprite;
		private var elementsBar:Sprite;		
		private var elementsBarMask:Sprite;		
		
		private var dropDownOpened:Boolean;		
		private var disabled:Boolean;		
		private var mouseOver:Boolean;
		
		/**
		 * constructor
		 * @param objects array of objects to be stored inside ComboBoxElements
		 * @param style style can be applied on constructor or from function setStyle
		 */
		public function ComboBox(objects:Array, style:ComboBoxStyle = null) {							
			
			this.objects = objects;
			this.style = (style != null) ? style : new ComboBoxStyle();	// default style if none specified in constructor
						
			this.disabled = false;
			this.mouseOver = false;			
			this.dropDownOpened = false;
			
			this.elements = new Array();					
			for each(var object:Object in objects) {
				this.elements.push(new ComboBoxElement(object));
			}										
			
			this.mainElement = new ComboBoxElement(this.objects[0]); // first element in the list
			this.mainElement.active = true; 
			
			this.addEventListener(Event.ADDED_TO_STAGE, stageReady);
		}
		
		private function stageReady(e:Event):void {			
			this.removeEventListener(Event.ADDED_TO_STAGE, stageReady);							
			
			this.stage.addEventListener(MouseEvent.MOUSE_DOWN, this.OuterClose, false, 99, true);
			this.addEventListener(MouseEvent.ROLL_OVER, this.rolledOver, false, 0, true);
			this.addEventListener(MouseEvent.ROLL_OUT, this.rolledOut, false, 0, true);
			
			this.drawComboBox(); 
		}
		
		/**
		 * Changes default style and redraws entire combobox 
		 * @param style new style to be applied
		 */
		public function setStyle(style:ComboBoxStyle):void {
			this.style = style;			
			this.drawComboBox();			
		}		
		
		private function rolledOver(e:Event):void {
			this.mouseOver = true;
		}
		
		private function rolledOut(e:Event):void {
			this.mouseOver = false;
		}
		
		/**
		 * closes combobox dropdown when clicking outside combobox
		 * @param e mouse down event
		 */
		private function OuterClose(e:Event):void {
			if (!this.mouseOver) {
				this.closeDropDown();
			}
		}
		
		/**
		 * builds entire combobox 
		 * applies style to all its elements
		 */
		private function drawComboBox():void {						
						
			for each(var element:ComboBoxElement in elements) {	
				element.setStyle(this.style);
			}
			
			this.mainElement.setStyle(this.style);
			this.mainElement.setMainElement(); // styles for main element
			
			this.addChild(this.mainElement);
			
			// building elements bar						
			this.elementsBar = new Sprite();	
			this.elementsBar.y = 0;			
			this.addChild(elementsBar);	
						
			var elementHeight:Number = this.mainElement.height;
			
			// build drop down button
			if (this.elements.length > 1) {				
				if (this.dropDownButton != null) {
					this.dropDownButton.graphics.clear(); 
				}				
				this.dropDownButton = new Sprite();								
				this.dropDownButton.graphics.beginFill(this.style.buttonBackgroundColor,this.style.mainElementAlpha);
				this.dropDownButton.graphics.drawRect(0, 0, elementHeight, elementHeight - ((this.style.elementBorder) ? 1 : 0) ); // inside border 
				if (this.style.elementBorder){
					this.dropDownButton.graphics.moveTo(0, 0);
					this.dropDownButton.graphics.lineStyle(1, this.style.elementBorderColor);
					this.dropDownButton.graphics.lineTo(0, elementHeight);
				}
				
				this.dropDownButton.graphics.endFill();
				
				if(this.style.buttonTriangleBitmapPlain == null){
					if (!this.style.opensUp) {
						this.dropDownButton.graphics.beginFill(this.style.buttonTriangleColor,this.style.mainElementAlpha);
						this.dropDownButton.graphics.moveTo(elementHeight/3, elementHeight/3 );  
						this.dropDownButton.graphics.lineTo(elementHeight/3*2, elementHeight/3 );
						this.dropDownButton.graphics.lineTo(elementHeight/2, elementHeight/3*2 );				
						this.dropDownButton.graphics.endFill();												
					}else {	
						this.dropDownButton.graphics.beginFill(this.style.buttonTriangleColor,this.style.mainElementAlpha);
						this.dropDownButton.graphics.moveTo(elementHeight/2, elementHeight/3 );  
						this.dropDownButton.graphics.lineTo(elementHeight/3*2, elementHeight/3*2 );
						this.dropDownButton.graphics.lineTo(elementHeight/3, elementHeight/3*2 );				
						this.dropDownButton.graphics.endFill();												
					}
				}else {					
					this.setDropDownButtonBitmap(false);							
				}
				this.dropDownButton.buttonMode = true;
				this.dropDownButton.useHandCursor = true;
				this.addChild(this.dropDownButton);	 	
				this.dropDownButton.addEventListener(MouseEvent.CLICK, openDropDown);							
			}			
			
			// adding elements to elementBar
			var lastY:Number = (!this.style.opensUp) ? this.style.elementSpacing : -this.style.elementSpacing;			
			for (var i:int = 0; i < elements.length; i++) {								
				element = elements[this.style.opensUp ? (elements.length-1-i):(i)] ;
				
				this.elementsBar.addChild(element);									
				if (element.object.label == this.mainElement.object.label) {
					element.active = true;					
					element.setSelectedBackground(true);
				}else {
					element.active = false;				
					element.setSelectedBackground(false);
				}												
				element.y = lastY;
				lastY += (!this.style.opensUp) ? element.height : - element.height;		
				lastY += (!this.style.opensUp) ? this.style.elementSpacing : -this.style.elementSpacing;
			}											
			
			var elementWidth:Number = 0;
			
			if(this.style.elementWidth != -1){											
				elementWidth = this.style.elementWidth;
			}else {
				// determine box width from longest label				
				for each(element in this.elements) {
					if (element.getTextWidth() > elementWidth) {
						elementWidth = element.getTextWidth();						
					}					
				}											
			}			
			
			// so that dropDownButton would fit inside mainElement
			elementWidth += (this.objects.length > 1) ? this.dropDownButton.width : 0;			
			elementWidth += this.style.marginLeft + this.style.marginRight;
			
			// setting width elements to widest element if not set in styles
			this.mainElement.setWidth(elementWidth); 						
			for each(element in elements) {
				element.setWidth(elementWidth);
			}			
			
			if(this.objects.length > 1){
				this.dropDownButton.x = this.mainElement.width - this.dropDownButton.width ;
				this.dropDownButton.y = (this.style.elementBorder) ? 1 : 0;			
			}else {
				this.mainElement.setDisabled(false);
			}
			
			this.elementsBar.y = (!this.style.opensUp) ? elementHeight - this.elementsBar.height - this.style.elementSpacing :  this.elementsBar.height - this.mainElement.height + this.style.elementSpacing; 
			
			this.elementsBarMask = new Sprite();						
			this.addChild(this.elementsBarMask);			
			this.setChildIndex(elementsBar, 0);			
			this.elementsBarMask.graphics.beginFill(0x000000);
			this.elementsBarMask.graphics.drawRect(0, 0, elementWidth+5,this.elementsBar.height +5 + this.style.elementSpacing); 			
			this.elementsBarMask.y = (!this.style.opensUp) ?  this.mainElement.height  : - this.elementsBarMask.height ;
			this.elementsBar.mask = this.elementsBarMask;			
		}		
		
		private function setDropDownButtonBitmap(type:Boolean):void {
			while (this.dropDownButton.numChildren) {
				this.dropDownButton.removeChildAt(0);
			}
			var buttonBitmap:Bitmap;
			if(!type && this.style.buttonTriangleBitmapPlain!=null){				
				buttonBitmap = new Bitmap(new this.style.buttonTriangleBitmapPlain().bitmapData);					
			}
			else if (this.style.buttonTriangleBitmapPress!=null) {
				buttonBitmap = new Bitmap(new this.style.buttonTriangleBitmapPress().bitmapData);					
			}			
			if(buttonBitmap!=null){
				buttonBitmap.x = this.dropDownButton.width * 0.5 - buttonBitmap.width * 0.5;
				buttonBitmap.y = this.dropDownButton.height * 0.5 - buttonBitmap.height * 0.5;
				this.dropDownButton.addChild(buttonBitmap);				
			}
		}
						
		private function openDropDown(e:Event=null):void {
			if(this.objects.length > 1){
				if (!this.dropDownOpened) {
					TweenLite.killTweensOf(this.elementsBar, true);										
					if (!this.style.opensUp) {												
						this.elementsBar.y = this.mainElement.height - this.elementsBar.height ; 												
						TweenLite.to(this.elementsBar, this.style.openingSpeed, { y:this.mainElement.height } );												
					}else{												
						this.elementsBar.y = this.elementsBar.height - this.mainElement.height;									
						TweenLite.to(this.elementsBar, this.style.openingSpeed, { y: - this.mainElement.height} ); 
					}
					this.dropDownOpened = true;
					this.setDropDownButtonBitmap(true);
				}else {
					this.closeDropDown();				
				}
			}
		}
		
		private function closeDropDown():void{						
			if (this.objects.length > 1 && this.dropDownOpened) {
				TweenLite.killTweensOf(this.elementsBar, true);				
				if (!this.style.opensUp) {										
					this.elementsBar.y = this.mainElement.height; 															
					TweenLite.to(this.elementsBar, this.style.openingSpeed, { y: this.mainElement.height - this.elementsBar.height - this.style.elementSpacing} );														
				}else {					
					this.elementsBar.y = -this.mainElement.height; 										
					TweenLite.to(this.elementsBar, this.style.openingSpeed, { y: this.elementsBar.height - this.mainElement.height  + this.style.elementSpacing} );					
				}
				this.dropDownOpened = false;	
				this.setDropDownButtonBitmap(false);
			}
		}
		
		public function setActive(object:Object):void {					
			if (this.mainElement.object.label == object.label) {
				if (this.dropDownOpened) {					
					this.closeDropDown();
				}else {
					this.openDropDown();
				}
			}else{			
				if(!this.disabled){
					this.closeDropDown();				
					this.repaintElements(object.label);					
					this.disabled = true;
					for each(var element:ComboBoxElement in this.elements) {
						element.setDisabled(false);
					}
					TweenLite.delayedCall(this.style.openingSpeed, this.unlock,[object]);					
					
				}else{
					if (this.dropDownOpened) {					
						this.closeDropDown();
					}else {
						this.openDropDown();
					}
				}
			}			
		}								
		
		public function setSelected(label:String):void {			
			this.repaintElements(label);			
		}
		
		
		private function repaintElements(label:String):void {
			for each(var element:ComboBoxElement in this.elements) {
				if (element.object.label == label) {
					element.active = true;					
					element.setSelectedBackground(true);
				}else {
					element.active = false;				
					element.setSelectedBackground(false);
				}															
			}															
			this.mainElement.object = this.getActiveObject(label);
			this.mainElement.refresh();
		}				
		
		
		public function enable():void {			
			for each(var element:ComboBoxElement in this.elements) {				
				element.setEnabled();				
			}															
			this.mainElement.setEnabled();
			this.disabled = false;
		}
		
		public function disable():void {
			for each(var element:ComboBoxElement in this.elements) {				
				element.setDisabled();				
			}															
			this.mainElement.setDisabled();
			this.disabled = true;
		}								
		
		private function unlock(object:Object):void {
			parent.dispatchEvent(new ComboBoxEvent(ComboBoxEvent.LABEL_CHANGED, object));				
			this.disabled = false;
			for each(var element:ComboBoxElement in this.elements) {
				element.setEnabled(false);
			}
		}		
		
		private function getActiveObject(label:String):Object {
			for each(var element:ComboBoxElement in this.elements) {
				if (element.object.label == label) {
					return element.object;
				}
			}
			return null;
		}		
		
		public function getActive():Object {
			return this.mainElement.object;
		}								
		
		public function getHeight():Number {
			return this.mainElement.height;
		}
		
		public function getWidth():Number {
			return this.mainElement.width;
		}		
	}	
}